package com.googlecode.layout4j.web;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parses layout tags and invokes the appropriate JspFrgament.
 * <p>
 * Various page attributes are set:
 * <li>Layout: layoutColspan</li>
 * <li>Row: layoutRowColspan</li>
 * <li>Column: layoutColumnColspan, layoutColumnIndex, layoutColumnType, XXXlayoutColumnLast</li>
 * <li>Content: layoutContentId, layoutContentStyle, layoutContentType, layoutContentWidth</li>
 * </p>
 * <p>XXX: Using tag variables would reduce potential naming clashes.
 * </p>
 * 
 * @see RenderTag
 * @author Matt Humphreys
 */
public class LayoutJspHandler extends DefaultHandler {
	public static final String LAYOUT_EL 	= "layout";
	public static final String COLUMN_EL 	= "column";
	public static final String ROW_EL 		= "row";
	public static final String CONTENT_EL 	= "content";
	
	private static Logger log = LoggerFactory.getLogger(LayoutJspHandler.class);
	private JspFragment layoutStart;
	private JspFragment layoutEnd;
	private JspFragment rowStart;
	private JspFragment rowEnd;
	private JspFragment columnStart;
	private JspFragment columnEnd;
	private JspFragment contentStart;
	private JspFragment contentEnd;
	private PageContext pageContext;
	private int elementDepth = 0;
	
	public LayoutJspHandler(PageContext ctx) {
		pageContext = ctx;
	}
	
	public void setLayoutStart(JspFragment frag) {
		layoutStart = frag;
	}
	public void setLayoutEnd(JspFragment frag) {
		layoutEnd = frag;
	}
	public void setRowStart(JspFragment frag) {
		rowStart = frag;
	}
	public void setRowEnd(JspFragment frag) {
		rowEnd = frag;
	}
	public void setColumnStart(JspFragment frag) {
		columnStart = frag;
	}
	public void setColumnEnd(JspFragment frag) {
		columnEnd = frag;
	}
	public void setContentStart(JspFragment frag) {
		contentStart = frag;
	}
	public void setContentEnd(JspFragment frag) {
		contentEnd = frag;
	}
	
	@Override
	public void startElement(String namespaceURI, String localName, String qName, Attributes attrs) 
			throws SAXException {
		
		elementDepth++;
		
		// REMEMBER to remove attributes when the tag goes out of scope
		
		pageContext.setAttribute("layoutElementDepth", elementDepth);
		if (qName.equals(LAYOUT_EL)) {
			// XXX: Handle non-existing attributes?
			pageContext.setAttribute("layoutMaxCols", attrs.getValue("max-cols"));
			invoke(layoutStart, pageContext.getOut());
			
		} else if (qName.equals(ROW_EL)) {
			pageContext.setAttribute("layoutRowMaxCols", attrs.getValue("max-cols"));
			invoke(rowStart, pageContext.getOut());
			
		} else if (qName.equals(COLUMN_EL)) {
			pageContext.setAttribute("layoutColumnMaxCols", attrs.getValue("max-cols"));
			pageContext.setAttribute("layoutColumnAside", attrs.getValue("aside"));
			pageContext.setAttribute("layoutColumnType", attrs.getValue("type"));
			invoke(columnStart, pageContext.getOut());

		} else if (qName.equals(CONTENT_EL)) {
			pageContext.setAttribute("layoutContentDataId", attrs.getValue("data-id"));
			pageContext.setAttribute("layoutContentStyleId", attrs.getValue("style"));
			pageContext.setAttribute("layoutContentDataTypeId", attrs.getValue("data-type"));
			// Width mainly used by layout editor (rather than public webapp)
			pageContext.setAttribute("layoutContentWidth", attrs.getValue("width"));
			invoke(contentStart, pageContext.getOut());
		}
	}
	
	@Override
	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
		pageContext.setAttribute("layoutElementDepth", elementDepth);
		if (qName.equals(LAYOUT_EL)) {
			// XXX: Handle non-existing attributes?
			
			invoke(layoutEnd, pageContext.getOut());
			pageContext.removeAttribute("layoutMaxCols", PageContext.PAGE_SCOPE);
			pageContext.removeAttribute("layoutElementDepth", PageContext.PAGE_SCOPE);
			
		} else if (qName.equals(ROW_EL)) {
			invoke(rowEnd, pageContext.getOut());
			pageContext.removeAttribute("layoutRowMaxCols", PageContext.PAGE_SCOPE);
			
		} else if (qName.equals(COLUMN_EL)) {
			invoke(columnEnd, pageContext.getOut());
			pageContext.removeAttribute("layoutColumnMaxCols", PageContext.PAGE_SCOPE);
			pageContext.removeAttribute("layoutColumnLast", PageContext.PAGE_SCOPE);

		} else if (qName.equals(CONTENT_EL)) {
			invoke(contentEnd, pageContext.getOut());
			pageContext.removeAttribute("layoutContentDataId", PageContext.PAGE_SCOPE);
			pageContext.removeAttribute("layoutContentStyleId", PageContext.PAGE_SCOPE);
			pageContext.removeAttribute("layoutContentDataTypeId", PageContext.PAGE_SCOPE);
		}
		elementDepth = elementDepth - 1;
	}

	private void invoke(JspFragment frag, JspWriter out) throws SAXException {
		if (frag != null) {
			try {
				frag.invoke(out);
			} catch (JspException e) {
				throw new SAXException(e);
			} catch (IOException e) {
				throw new SAXException(e);
			}
		}
	}
}
